if(!require("plotly")) {install.packages("plotly")}
# install.packages("latex2exp")
# install.packages("BiocManager")
# install.packages("corrplot")
# BiocManager::install("EBImage")
if(!require("lme4")){install.packages("lme4")}
if(!require("lmerTest")){install.packages("lmerTest")}
if(!require("nlme")){install.packages("nlme")}
if(!require("formattable")){install.packages("formattable")}
if(!require("xgboost")){install.packages("xgboost")}
if(!require("processx")) {install.packages("processx")}
library(plotly)
library(lme4)
library(lmerTest)
library(nlme)
library(formattable)
library(xgboost)
### Load libraries
library(EBImage)
library(ggplot2)
library(stringr)
library(gridExtra)
library(latex2exp)
packageVersion('plotly')
[1] ‘4.9.1’
Sys.setenv("plotly_username"="thuynh32")
Sys.setenv("plotly_api_key"="xcSv1yzujDc1IGEwQlr2")
colorBlue = "#007fff"
colorRed = "#ff7f7f"
colorGray = "#cccccc"
colorGreen = "#11ff00"
all_Drive4 <- read.csv('../../../data/TT1/preprocessed/All/TT1_Drive_4_1s_1s.csv')
all_Drive4$Subject <- as.factor(all_Drive4$Subject)
all_Drive4$logPerspiration <- log(all_Drive4$Perspiration)
persons = c("01", "02", "03", "04", "05", "06", "07", "09", "12", "13", "15", "16", "17", "18", "22", "24", "29", "30", "31", "32", "41")
starting_points = c( 62 , 83 , 70 , 69 , 71 , 61 , 69 , 74 , 65 , 61 , 64 , 63 , 74 , 65 , 79 , 64 , 69 , 69 , 67 , 65 , 62 )
peak_points = c( 65 , 86 , 73 , 73 , 73 , 64 , 73 , 79 , 69 , 64 , 68 , 67 , 77 , 68 , 82 , 67 , 72 , 72 , 71 , 68 , 64 )
ending_points = c( 67 , 89 , 75 , 76 , 75 , 67 , 75 , 82 , 74 , 66 , 73 , 70 , 80 , 71 , 85 , 70 , 76 , 75 , 75 , 70 , 66 )
# Driving time
driving_times = vector(mode="list", length = length(persons))
names(driving_times) <- persons
complete_times = vector(mode="list", length = length(persons))
names(complete_times) <- persons
data_baseline = vector(mode="list", length=length(persons))
pp_baseline = vector(mode="list", length=length(persons))
names(data_baseline) <- persons
names(pp_baseline) <- persons
# Number of peaks
TRACKIN_DURATION = 10
DRIVE_MODE = 4
idx <- 1
plt_AllAcc <- vector(mode="list", length=length(persons))
names(plt_AllAcc) <- persons
COLOR_ACC = "#02A3C8"
COLOR_PP = "#F28E8E"
COLOR_BRAKE = "#888888"
y1 <- list(
tickfont = list(color = COLOR_ACC),
title="Degree",
range=c(0, 100)
)
y2 <- list(
tickfont = list(color = COLOR_PP),
overlaying = "y",
side = "right",
title = "Log Perspiration",
showgrid = FALSE,
range=c(min(all_Drive4$ppLogNormalized), max(all_Drive4$ppLogNormalized))
)
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
# Baseline
data_baseline[[p]] <- read.csv(str_interp("../../../data/TT1/preprocessed/T0${person}/T0${person}_Drive_1.csv", list(person=p)))
# Compute the mean
p_pp_nr <- data_baseline[[p]]$Perspiration
p_pp_nr <- p_pp_nr[!is.na(p_pp_nr)]
pp_baseline[[p]] <- log(mean(p_pp_nr))
# Incident
driving_times[[p]] <- max(pData$Time)
complete_times[[p]] <- ifelse(starting_points[idx] + TRACKIN_DURATION > driving_times[[p]], driving_times[[p]], starting_points[idx] + TRACKIN_DURATION)
incident_starting_time <- starting_points[idx]
incident_ending_time <- ending_points[idx]
complete_time <- complete_times[[p]]
from_time <- ifelse(incident_starting_time - 30 >= 0, incident_starting_time - 30, 0)
to_time <- complete_time
pDataBefore <- pData[pData$Time < incident_starting_time & pData$Time >= from_time,]
pDataAfter <- pData[pData$Time >= (incident_starting_time + 3) & pData$Time <= to_time,]
ppMeanBefore <- mean(pDataBefore$ppLogNormalized)
ppMeanAfter <- mean(pDataAfter$ppLogNormalized)
dir.create(file.path('../figures/drive/', paste0('Drive_', DRIVE_MODE)), showWarnings = FALSE)
fname <- str_interp('../figures/drive/Drive_${drive}/P${person}.svg', list(drive=DRIVE_MODE, person=p))
pData <- pData[pData$Time >= from_time & pData$Time <= to_time,]
plot_Acc <- plot_ly(pData, x = ~Time) %>%
add_trace(name="Acceleration", y = ~Acceleration, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_ACC)) %>%
add_trace(name="Brake", y = ~Braking, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_BRAKE)) %>%
add_trace(name="PP", y = ~ppLogNormalized, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_PP), yaxis = "y2") %>%
add_segments(x = min(pData$Time), xend = max(pData$Time), y = ppMeanBefore, yend = ppMeanBefore,
yaxis = "y2", name="Mean PP (Before Incident)",
line=list(color=COLOR_PP, dash = 'dot')) %>%
add_segments(x = min(pData$Time), xend = max(pData$Time), y = ppMeanAfter, yend = ppMeanAfter,
yaxis = "y2", name="Mean PP (After Incident)",
line=list(color="darkred", dash = 'dot')) %>%
# add_segments(x = min(pData$Time) - 0.1, xend = max(pData$Time), y = pp_baseline[[p]], yend = pp_baseline[[p]],
# yaxis = "y2", name="Baseline PP (from Drive 1)",
# line=list(color="blue", dash = 'dot')) %>%
layout(
title=paste("Subject #", p),
xaxis=list(title="Time [s]"),
yaxis=y1 ,
yaxis2=y2,
margin = list(l = 50, r = 50, b = 50, t = 50, pad = 4),
shapes = list(
list(type = "rect", fillcolor = "red",
line = list(color = "red"), opacity = 0.3,
x0 = starting_points[idx], x1 = ending_points[idx], xref = "x",
y0 = 0, y1 = 100, yref = "y"),
list(type = "rect", fillcolor = "pink",
line = list(color = "pink"), opacity = 0.3,
x0 = starting_points[idx], x1 = complete_times[[p]], xref = "x",
y0 = 0, y1 = 100, yref = "y")
),
legend = list(x = min(pData$Time), y = 60)
)
# orca(plot_PP, fname)
idx <- idx + 1
plt_AllAcc[[p]] <- plot_Acc
}
htmltools::tagList(plt_AllAcc)
idx <- 1
behavioralColumns <- c("Subject",
"Brake_u",
"Brake_std",
"PP_before",
"PP_u",
"PP_std",
"PP_dev")
behavioralMatrix <- matrix(nrow=length(persons), ncol = length(behavioralColumns))
# Careful about Subject 09
# selected_persons <- persons[persons != "09"]
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
starting_time <- starting_points[idx]
ending_time <- ending_points[idx]
complete_time <- complete_times[[p]]
from_time <- ifelse(starting_time - 30 >= 0, starting_time - 30, 0)
to_time <- complete_time
dfBefore <- pData[pData$Time < starting_points[idx] & pData$Time >= from_time,]
dfAfter <- pData[pData$Time >= starting_points[idx] + 3 & pData$Time <= to_time,]
# diffSpeed <- mean(dfAfter$Speed) - mean(dfBefore$Speed)
brakeMean <- mean(dfAfter$Braking)
brakeStd <- mean(dfAfter$Braking)
ppMean <- mean(dfAfter$ppLogNormalized)
ppBefore <- mean(dfBefore$ppLogNormalized)
ppStd <- sd(dfAfter$ppLogNormalized)
mid_avg <- (pp_baseline[[p]] + mean(dfBefore$ppLogNormalized)) / 2
diffPP <- mean(dfAfter$ppLogNormalized) - mean(dfBefore$ppLogNormalized)
behavioralMatrix[idx, ] <- c(p,
round(brakeMean, digits=5),
round(brakeStd, digits=5),
round(ppBefore, digits=5),
round(ppMean, digits = 5),
round(ppStd, digits=5),
round(diffPP, digits=5))
idx <- idx + 1
}
# behavioralMatrix
behavioralDf <- as.data.frame(behavioralMatrix)
names(behavioralDf) <- behavioralColumns
behavioralDf
NA
clusteringDf <- behavioralDf
clusteringDf$Subject <- NULL
# clusteringDf$PP_dev_norm <- as.numeric(clusteringDf$PP_dev) / as.numeric(clusteringDf$PP_u)
# clusteringDf$PP_std_norm <- as.numeric(clusteringDf$PP_std) / as.numeric(clusteringDf$PP_u)
clusteringDf$Brake_u <- NULL
clusteringDf$Brake_std <- NULL
clusteringDf$PP_before <- NULL
clusteringDf$PP_u <- NULL
clusteringDf$PP_std <- NULL
# clusteringDf$PP_dev <- NULL
rownames(clusteringDf) <- paste0("#", persons)
for (col in names(clusteringDf)) {
clusteringDf[,col] <- as.numeric(as.character(clusteringDf[, col]))
clusteringDf[,col] <- scale(clusteringDf[,col])
}
clusteringDf
library(dendextend)
NUMBER_OF_CLUSTERS = 3
color_darkpink = "#e75480"
CLUSTER_BRANCH_COLORS <- c("red", color_darkpink, "blue")[1:NUMBER_OF_CLUSTERS]
CLUSTER_LABEL_COLORS <- c("red", color_darkpink, "blue")[1:NUMBER_OF_CLUSTERS]
behavioralMatrixClustering <- as.matrix(clusteringDf)
rownames(behavioralMatrixClustering) <- paste0("#", persons)
distMatrix <- dist(behavioralMatrixClustering)
hresults <- distMatrix %>% hclust
hc <- hresults %>%
as.dendrogram %>%
set("nodes_cex", NUMBER_OF_CLUSTERS) %>%
set("labels_col", value = CLUSTER_LABEL_COLORS, k=NUMBER_OF_CLUSTERS) %>%
# set("leaves_pch", 19) %>%
# set("leaves_col", value = c("gray"), k=NUMBER_OF_CLUSTERS) %>%
set("branches_k_color", value=CLUSTER_BRANCH_COLORS, k=NUMBER_OF_CLUSTERS)
plot(hc)
legend("topright",
legend = c("Exceptional Chicken" , "Normal Chicken" , "No-change/Cool"),
col = c("red", "pink" , "blue"),
pch = c(20,20,20), bty = "n", pt.cex = 1.5, cex = 0.8 ,
text.col = "black", horiz = FALSE, inset = c(0, 0.1))

dfActivity <- all_Drive4[all_Drive4$Time==60,] %>% select(c("Subject", "Activity"))
getActivityName <- function(x) {
if(x == 1) return("Normal")
if(x == 2) return("Cognitive")
if(x == 3) return("Motoric")
}
dfActivity$ActivityName <- sapply(dfActivity$Activity, getActivityName)
dfActivity$Subject <- as.factor(dfActivity$Subject)
rownames(dfActivity) <- NULL
dfActivity %>% select(c("Subject", "ActivityName"))
library(cluster)
fit <- kmeans(clusteringDf, 2)
clusplot(clusteringDf, fit$cluster, color=TRUE, shade=TRUE,
labels=2, lines=0)

silhouette_score <- function(k){
km <- kmeans(clusteringDf, centers = k, nstart=25)
ss <- silhouette(km$cluster, dist(clusteringDf))
mean(ss[, 3])
}
k <- 2:10
avg_sil <- sapply(k, silhouette_score)
plot(k, type='b', avg_sil, xlab='Number of clusters', ylab='Average Silhouette Scores', frame=FALSE)
# plot(clusteringDf$PP_Dev)
# ggplot(clusteringDf, aes(PP_Mean)) +
# geom_density(alpha = 0.3)
# plot(clusteringDf$PP_Dev)
# ggplot(clusteringDf, aes(PP_Dev)) +
# geom_density(alpha = 0.3)
ML Model
df_Drive3 <- read.csv('../data/output/Drive_3/corr_Prev_15s_Next_5s.csv')
df_Drive3$Subject <- as.factor(df_Drive3$Subject)
CLUSTER_THRESHOLD = 4
clusters <- cutree(hresults, h=CLUSTER_THRESHOLD)
getClusterName <- function(s) {
sID <- str_replace(s, "Subject ", "")
if (str_sub(sID[1], 1,1) != "#") {
sID <- paste0("#", sID)
}
return(ifelse(clusters[sID] == 1, 1, 0))
}
colClass <- sapply(persons, getClusterName)
df_Drive3$clsPP <- as.factor(colClass)
# install.packages("randomForest")
# install.packages("MLmetrics")
# install.packages("caret")
library(randomForest)
library(ROCR)
library(caret)
# Train
trainAndTestModel <- function(pData) {
pSelected <- pData
pSelected$Subject <- NULL
# pSelected$`Steering..std..` <- NULL
# View(pSelected)
nChicken = nrow(pSelected[pSelected$clsPP == 1,])
nNormal = nrow(pSelected[pSelected$clsPP == 0,])
print(paste("Chicken =", nChicken))
print(paste("Normal =", nNormal))
# Split dataset
set.seed(43)
n_folds = 4
folds <- createFolds(factor(pSelected$clsPP), k = n_folds, list = FALSE)
# folds <- cut(seq(1,nrow(pSelected)), breaks=n_folds,labels=FALSE)
pSelected$fold <- folds
kf_acc <- double(n_folds)
kf_prec <- double(n_folds)
kf_recall <- double(n_folds)
kf_f1 <- double(n_folds)
kf_npv <- double(n_folds)
for (i in 1:n_folds) {
# train_ind <- sample(seq_len(nrow(pSelected)), size = smp_size)
# train <- pSelected[train_ind, ]
# test <- pSelected[-train_ind, ]
# test_ind <- which(folds==i, arr.ind=T)
train <- pSelected[pSelected$fold != i, ] %>% select(-fold)
test <- pSelected[pSelected$fold == i, ] %>% select(-fold)
print(paste("Train =", nrow(train), "Pos =", nrow(train[train$clsPP == 1,]), "Neg =", nrow(train[train$clsPP == 0,])))
print(paste("Test =", nrow(test), "Pos =", nrow(test[test$clsPP == 1,]), "Neg =", nrow(test[test$clsPP == 0,])))
# Model Train
model <- randomForest(clsPP~., data = train, importance = TRUE, ntree=10)
print(importance(model))
# print(model)
# Test
testX <- select(test, -clsPP)
predY <- predict(model, testX)
testY <- test$clsPP
#print(levels(predY))
#print(levels(testY))
# Evaluate
# print(table(predY, testY))
kf_acc[i] <- mean(predY==testY)
kf_recall[i] <- sensitivity(predY, testY)
kf_prec[i] <- posPredValue(predY, testY, positive = 1)
kf_f1[i] <- (2 * kf_recall[i] * kf_prec[i]) / (kf_recall[i] + kf_prec[i])
kf_npv[i] <- negPredValue(predY, testY, positive = 1)
# print(paste("Perf:", kf_acc[i], kf_recall[i], kf_prec[i], kf_f1[i], kf_npv[i]))
}
# XGB
param <- list(objective = "binary:logistic",
booster = "gbtree",
eval_metric = "auc",
eta = 0.1,
max_depth = 5,
gamma =0.8,
min_child_weight = 3,
subsample = 1,
colsample_bytree = 0.5,
stratified = F
)
pSelected <- pSelected %>% mutate(clsPP=ifelse(clsPP==1, 1, 0))
# AUC
# aucs = c()
# xgb_m = xgb.cv( params = param,
# data = as.matrix(pSelected %>% select(-clsPP)) ,
# label = pSelected$clsPP,
# nrounds = 500,
# verbose = F,
# prediction = T,
# maximize = T,
# nfold = n_folds,
# metrics = "auc",
# early_stopping_rounds = 100,
# scale_pos_weight = 1)
# aucs = c(aucs,as.numeric(xgb_m$evaluation_log[xgb_m$best_iteration,"test_auc_mean"]))
# Get average performance
acc <- mean(kf_acc)
prec <- mean(kf_prec)
recall <- mean(kf_recall)
f1 <- mean(kf_f1)
npv <- mean(kf_npv)
# auc <- mean(aucs)
# Return
rtn <- list(
accuracy=acc,
recall=recall,
precision=prec,
f1=f1,
npv=npv
# auc=auc
)
print(rtn)
}
trainAndTestModel(df_Drive3)
library(caret)
library(VGAM)
NUM_FEATURES = 8
pSelected <- df_Drive3
pSelected$Subject <- NULL
fit <- vglm(clsPP~., family=multinomial, data=pSelected)
# summarize the fit
summary(fit)
probabilities <- predict(fit, pSelected[,1:NUM_FEATURES], type="response")
predictions <- apply(probabilities, 1, which.max)
predictions[which(predictions=="High")] <- levels(pSelected$clsPP)[1]
predictions[which(predictions=="Low")] <- levels(pSelected$clsPP)[2]
# summarize accuracy
table(predictions, pSelected$clsPP)
# x <- pSelected[,1:NUM_FEATURES]
# y <- pSelected[,NUM_FEATURES + 1]
# # fit model
# fit <- plsda(x, y, probMethod="Bayes")
# # summarize the fit
# summary(fit)
# # make predictions
# predictions <- predict(fit, pSelected[,1:NUM_FEATURES])
# # summarize accuracy
# table(predictions, pSelected$clsPP)
library(caret)
pSelected <- df_Drive3
pSelected$Subject <- NULL
pSelected$clsPP <- as.numeric(colClass)
# define training control
train_control <- trainControl(method = "cv", number = 4)
# train the model on training set
model <- train(clsPP ~ .,
data = pSelected,
trControl = train_control,
method = "glm",
family=binomial())
# print cv scores
summary(model)
predTrain = predict(model, newdata=pSelected, type="raw")
table(pSelected$clsPP, predTrain > 0.5)
formula = clsPP ~ .
modellm <- glm(formula, data=pSelected)
plot(modellm)
summary(model)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmlmKCFyZXF1aXJlKCJwbG90bHkiKSkge2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpfQoKIyBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQojIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikgCiMgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJFQkltYWdlIikKCmlmKCFyZXF1aXJlKCJsbWU0Iikpe2luc3RhbGwucGFja2FnZXMoImxtZTQiKX0KaWYoIXJlcXVpcmUoImxtZXJUZXN0Iikpe2luc3RhbGwucGFja2FnZXMoImxtZXJUZXN0Iil9CmlmKCFyZXF1aXJlKCJubG1lIikpe2luc3RhbGwucGFja2FnZXMoIm5sbWUiKX0KaWYoIXJlcXVpcmUoImZvcm1hdHRhYmxlIikpe2luc3RhbGwucGFja2FnZXMoImZvcm1hdHRhYmxlIil9CmlmKCFyZXF1aXJlKCJ4Z2Jvb3N0Iikpe2luc3RhbGwucGFja2FnZXMoInhnYm9vc3QiKX0KaWYoIXJlcXVpcmUoInByb2Nlc3N4IikpIHtpbnN0YWxsLnBhY2thZ2VzKCJwcm9jZXNzeCIpfQoKbGlicmFyeShwbG90bHkpCmxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKbGlicmFyeShubG1lKQpsaWJyYXJ5KGZvcm1hdHRhYmxlKQpsaWJyYXJ5KHhnYm9vc3QpCgojIyMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShFQkltYWdlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobGF0ZXgyZXhwKQpwYWNrYWdlVmVyc2lvbigncGxvdGx5JykKU3lzLnNldGVudigicGxvdGx5X3VzZXJuYW1lIj0idGh1eW5oMzIiKQpTeXMuc2V0ZW52KCJwbG90bHlfYXBpX2tleSI9InhjU3YxeXp1akRjMUlHRXdRbHIyIikKCmBgYAoKYGBge3J9CmNvbG9yQmx1ZSA9ICIjMDA3ZmZmIgpjb2xvclJlZCA9ICIjZmY3ZjdmIgpjb2xvckdyYXkgPSAiI2NjY2NjYyIKY29sb3JHcmVlbiA9ICIjMTFmZjAwIgoKYWxsX0RyaXZlNCA8LSByZWFkLmNzdignLi4vLi4vLi4vZGF0YS9UVDEvcHJlcHJvY2Vzc2VkL0FsbC9UVDFfRHJpdmVfNF8xc18xcy5jc3YnKQphbGxfRHJpdmU0JFN1YmplY3QgPC0gYXMuZmFjdG9yKGFsbF9Ecml2ZTQkU3ViamVjdCkKYWxsX0RyaXZlNCRsb2dQZXJzcGlyYXRpb24gPC0gbG9nKGFsbF9Ecml2ZTQkUGVyc3BpcmF0aW9uKQoKcGVyc29ucyAgICAgICAgID0gYygiMDEiLCAiMDIiLCAiMDMiLCAiMDQiLCAiMDUiLCAiMDYiLCAiMDciLCAiMDkiLCAiMTIiLCAiMTMiLCAiMTUiLCAiMTYiLCAiMTciLCAiMTgiLCAiMjIiLCAiMjQiLCAiMjkiLCAiMzAiLCAiMzEiLCAiMzIiLCAiNDEiKQpzdGFydGluZ19wb2ludHMgPSBjKCA2MiAsICA4MyAsICA3MCAsICA2OSAsICA3MSAsICA2MSAsICA2OSAsICA3NCAsICA2NSAsICA2MSAsICA2NCAsICA2MyAsICA3NCAsICA2NSAsICA3OSAsICA2NCAsICA2OSAsICA2OSAsICA2NyAsICA2NSAsICA2MiApCnBlYWtfcG9pbnRzICAgICA9IGMoIDY1ICwgIDg2ICwgIDczICwgIDczICwgIDczICwgIDY0ICwgIDczICwgIDc5ICwgIDY5ICwgIDY0ICwgIDY4ICwgIDY3ICwgIDc3ICwgIDY4ICwgIDgyICwgIDY3ICwgIDcyICwgIDcyICwgIDcxICwgIDY4ICwgIDY0ICkKZW5kaW5nX3BvaW50cyAgID0gYyggNjcgLCAgODkgLCAgNzUgLCAgNzYgLCAgNzUgLCAgNjcgLCAgNzUgLCAgODIgLCAgNzQgLCAgNjYgLCAgNzMgLCAgNzAgLCAgODAgLCAgNzEgLCAgODUgLCAgNzAgLCAgNzYgLCAgNzUgLCAgNzUgLCAgNzAgLCAgNjYgKQoKIyBEcml2aW5nIHRpbWUKZHJpdmluZ190aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhkcml2aW5nX3RpbWVzKSA8LSBwZXJzb25zCgpjb21wbGV0ZV90aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhjb21wbGV0ZV90aW1lcykgPC0gcGVyc29ucwoKZGF0YV9iYXNlbGluZSA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkKcHBfYmFzZWxpbmUgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgocGVyc29ucykpCgpuYW1lcyhkYXRhX2Jhc2VsaW5lKSA8LSBwZXJzb25zCm5hbWVzKHBwX2Jhc2VsaW5lKSA8LSBwZXJzb25zCgojIE51bWJlciBvZiBwZWFrcwoKVFJBQ0tJTl9EVVJBVElPTiA9IDEwCkRSSVZFX01PREUgPSA0CmBgYAoKYGBge3J9CmlkeCA8LSAxCnBsdF9BbGxBY2MgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHBlcnNvbnMpKSAKbmFtZXMocGx0X0FsbEFjYykgPC0gcGVyc29ucwoKQ09MT1JfQUNDID0gIiMwMkEzQzgiCkNPTE9SX1BQID0gIiNGMjhFOEUiCkNPTE9SX0JSQUtFID0gIiM4ODg4ODgiCgp5MSA8LSBsaXN0KAogIHRpY2tmb250ID0gbGlzdChjb2xvciA9IENPTE9SX0FDQyksCiAgdGl0bGU9IkRlZ3JlZSIsCiAgcmFuZ2U9YygwLCAxMDApCikKeTIgPC0gbGlzdCgKICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSBDT0xPUl9QUCksCiAgb3ZlcmxheWluZyA9ICJ5IiwKICBzaWRlID0gInJpZ2h0IiwKICB0aXRsZSA9ICJMb2cgUGVyc3BpcmF0aW9uIiwKICBzaG93Z3JpZCA9IEZBTFNFLAogIHJhbmdlPWMobWluKGFsbF9Ecml2ZTQkcHBMb2dOb3JtYWxpemVkKSwgbWF4KGFsbF9Ecml2ZTQkcHBMb2dOb3JtYWxpemVkKSkKKQogIApmb3IgKHAgaW4gcGVyc29ucykgewogIHBEYXRhIDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRTdWJqZWN0PT1hcy5pbnRlZ2VyKHApIHwgYWxsX0RyaXZlNCRTdWJqZWN0PT1wLF0KICAKICAjIEJhc2VsaW5lCiAgZGF0YV9iYXNlbGluZVtbcF1dIDwtIHJlYWQuY3N2KHN0cl9pbnRlcnAoIi4uLy4uLy4uL2RhdGEvVFQxL3ByZXByb2Nlc3NlZC9UMCR7cGVyc29ufS9UMCR7cGVyc29ufV9Ecml2ZV8xLmNzdiIsIGxpc3QocGVyc29uPXApKSkKICAjIENvbXB1dGUgdGhlIG1lYW4KICBwX3BwX25yIDwtIGRhdGFfYmFzZWxpbmVbW3BdXSRQZXJzcGlyYXRpb24KICBwX3BwX25yIDwtIHBfcHBfbnJbIWlzLm5hKHBfcHBfbnIpXQogIHBwX2Jhc2VsaW5lW1twXV0gPC0gbG9nKG1lYW4ocF9wcF9ucikpCiAgCiAgIyBJbmNpZGVudAogIGRyaXZpbmdfdGltZXNbW3BdXSA8LSBtYXgocERhdGEkVGltZSkKICBjb21wbGV0ZV90aW1lc1tbcF1dIDwtIGlmZWxzZShzdGFydGluZ19wb2ludHNbaWR4XSArIFRSQUNLSU5fRFVSQVRJT04gPiBkcml2aW5nX3RpbWVzW1twXV0sIGRyaXZpbmdfdGltZXNbW3BdXSwgc3RhcnRpbmdfcG9pbnRzW2lkeF0gKyBUUkFDS0lOX0RVUkFUSU9OKQogIAogIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgPC0gc3RhcnRpbmdfcG9pbnRzW2lkeF0KICBpbmNpZGVudF9lbmRpbmdfdGltZSA8LSBlbmRpbmdfcG9pbnRzW2lkeF0KICBjb21wbGV0ZV90aW1lIDwtIGNvbXBsZXRlX3RpbWVzW1twXV0KICAKICBmcm9tX3RpbWUgPC0gaWZlbHNlKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgLSAzMCA+PSAwLCBpbmNpZGVudF9zdGFydGluZ190aW1lIC0gMzAsIDApCiAgdG9fdGltZSA8LSBjb21wbGV0ZV90aW1lCiAgCiAgICAKICBwRGF0YUJlZm9yZSA8LSBwRGF0YVtwRGF0YSRUaW1lIDwgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAmIHBEYXRhJFRpbWUgPj0gZnJvbV90aW1lLF0KICBwRGF0YUFmdGVyIDwtIHBEYXRhW3BEYXRhJFRpbWUgPj0gKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgKyAzKSAmIHBEYXRhJFRpbWUgPD0gdG9fdGltZSxdCiAgCiAgcHBNZWFuQmVmb3JlIDwtIG1lYW4ocERhdGFCZWZvcmUkcHBMb2dOb3JtYWxpemVkKQogIHBwTWVhbkFmdGVyIDwtIG1lYW4ocERhdGFBZnRlciRwcExvZ05vcm1hbGl6ZWQpCiAgCiAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoJy4uL2ZpZ3VyZXMvZHJpdmUvJywgcGFzdGUwKCdEcml2ZV8nLCBEUklWRV9NT0RFKSksIHNob3dXYXJuaW5ncyA9IEZBTFNFKQogIGZuYW1lIDwtIHN0cl9pbnRlcnAoJy4uL2ZpZ3VyZXMvZHJpdmUvRHJpdmVfJHtkcml2ZX0vUCR7cGVyc29ufS5zdmcnLCBsaXN0KGRyaXZlPURSSVZFX01PREUsIHBlcnNvbj1wKSkgCiAgCiAgcERhdGEgPC0gcERhdGFbcERhdGEkVGltZSA+PSBmcm9tX3RpbWUgJiBwRGF0YSRUaW1lIDw9IHRvX3RpbWUsXQogIHBsb3RfQWNjIDwtIHBsb3RfbHkocERhdGEsIHggPSB+VGltZSkgJT4lCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IkFjY2VsZXJhdGlvbiIsIHkgPSB+QWNjZWxlcmF0aW9uLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywgbGluZT1saXN0KHdpZHRoPTEuNSwgY29sb3I9Q09MT1JfQUNDKSkgJT4lIAogICAgICAgICAgICAgIGFkZF90cmFjZShuYW1lPSJCcmFrZSIsIHkgPSB+QnJha2luZywgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsIGxpbmU9bGlzdCh3aWR0aD0xLjUsIGNvbG9yPUNPTE9SX0JSQUtFKSkgJT4lCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IlBQIiwgeSA9IH5wcExvZ05vcm1hbGl6ZWQsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLCBsaW5lPWxpc3Qod2lkdGg9MS41LCBjb2xvcj1DT0xPUl9QUCksIHlheGlzID0gInkyIikgJT4lIAogICAgICAgICAgICAgIGFkZF9zZWdtZW50cyh4ID0gbWluKHBEYXRhJFRpbWUpLCB4ZW5kID0gbWF4KHBEYXRhJFRpbWUpLCB5ID0gcHBNZWFuQmVmb3JlLCB5ZW5kID0gcHBNZWFuQmVmb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSAieTIiLCBuYW1lPSJNZWFuIFBQIChCZWZvcmUgSW5jaWRlbnQpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZT1saXN0KGNvbG9yPUNPTE9SX1BQLCBkYXNoID0gJ2RvdCcpKSAlPiUKICAgICAgICAgICAgICBhZGRfc2VnbWVudHMoeCA9IG1pbihwRGF0YSRUaW1lKSwgeGVuZCA9IG1heChwRGF0YSRUaW1lKSwgeSA9IHBwTWVhbkFmdGVyLCB5ZW5kID0gcHBNZWFuQWZ0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9ICJ5MiIsIG5hbWU9Ik1lYW4gUFAgKEFmdGVyIEluY2lkZW50KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj0iZGFya3JlZCIsIGRhc2ggPSAnZG90JykpICU+JQogICAgICAgICAgICAgICMgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkVGltZSkgLSAwLjEsIHhlbmQgPSBtYXgocERhdGEkVGltZSksIHkgPSBwcF9iYXNlbGluZVtbcF1dLCB5ZW5kID0gcHBfYmFzZWxpbmVbW3BdXSwgCiAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgeWF4aXMgPSAieTIiLCBuYW1lPSJCYXNlbGluZSBQUCAoZnJvbSBEcml2ZSAxKSIsCiAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgbGluZT1saXN0KGNvbG9yPSJibHVlIiwgZGFzaCA9ICdkb3QnKSkgJT4lCiAgICAKICAgICAgICAgICAgICBsYXlvdXQoCiAgICAgICAgICAgICAgICB0aXRsZT1wYXN0ZSgiU3ViamVjdCAjIiwgcCksIAogICAgICAgICAgICAgICAgeGF4aXM9bGlzdCh0aXRsZT0iVGltZSBbc10iKSwgCiAgICAgICAgICAgICAgICB5YXhpcz15MSAsIAogICAgICAgICAgICAgICAgeWF4aXMyPXkyLCAKICAgICAgICAgICAgICAgIG1hcmdpbiA9IGxpc3QobCA9IDUwLCByID0gNTAsIGIgPSA1MCwgdCA9IDUwLCBwYWQgPSA0KSwKICAgICAgICAgICAgICAgIHNoYXBlcyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9ICJyZWN0IiwgZmlsbGNvbG9yID0gInJlZCIsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIpLCBvcGFjaXR5ID0gMC4zLAogICAgICAgICAgICAgICAgICAgICAgeDAgPSBzdGFydGluZ19wb2ludHNbaWR4XSwgeDEgPSBlbmRpbmdfcG9pbnRzW2lkeF0sIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKSwKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAicGluayIsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInBpbmsiKSwgb3BhY2l0eSA9IDAuMywKICAgICAgICAgICAgICAgICAgICAgIHgwID0gc3RhcnRpbmdfcG9pbnRzW2lkeF0sIHgxID0gY29tcGxldGVfdGltZXNbW3BdXSwgeHJlZiA9ICJ4IiwKICAgICAgICAgICAgICAgICAgICAgIHkwID0gMCwgeTEgPSAxMDAsIHlyZWYgPSAieSIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gbWluKHBEYXRhJFRpbWUpLCB5ID0gNjApCiAgICAgICAgICAgICAgKQogIAogICMgb3JjYShwbG90X1BQLCBmbmFtZSkKICBpZHggPC0gaWR4ICsgMQogIHBsdF9BbGxBY2NbW3BdXSA8LSBwbG90X0FjYwp9CgpodG1sdG9vbHM6OnRhZ0xpc3QocGx0X0FsbEFjYykKYGBgCgoKYGBge3J9CmlkeCA8LSAxCmJlaGF2aW9yYWxDb2x1bW5zIDwtIGMoIlN1YmplY3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAiQnJha2VfdSIsIAogICAgICAgICAgICAgICAgICAgICAgICJCcmFrZV9zdGQiLCAKICAgICAgICAgICAgICAgICAgICAgICAiUFBfYmVmb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAiUFBfdSIsICAKICAgICAgICAgICAgICAgICAgICAgICAiUFBfc3RkIiwKICAgICAgICAgICAgICAgICAgICAgICAiUFBfZGV2IikKYmVoYXZpb3JhbE1hdHJpeCA8LSBtYXRyaXgobnJvdz1sZW5ndGgocGVyc29ucyksIG5jb2wgPSBsZW5ndGgoYmVoYXZpb3JhbENvbHVtbnMpKQoKIyBDYXJlZnVsIGFib3V0IFN1YmplY3QgMDkKIyBzZWxlY3RlZF9wZXJzb25zIDwtIHBlcnNvbnNbcGVyc29ucyAhPSAiMDkiXQoKZm9yIChwIGluIHBlcnNvbnMpIHsKICBwRGF0YSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkU3ViamVjdD09YXMuaW50ZWdlcihwKSB8IGFsbF9Ecml2ZTQkU3ViamVjdD09cCxdCiAgCiAgc3RhcnRpbmdfdGltZSA8LSBzdGFydGluZ19wb2ludHNbaWR4XQogIGVuZGluZ190aW1lIDwtIGVuZGluZ19wb2ludHNbaWR4XQogIGNvbXBsZXRlX3RpbWUgPC0gY29tcGxldGVfdGltZXNbW3BdXSAKICAKICBmcm9tX3RpbWUgPC0gaWZlbHNlKHN0YXJ0aW5nX3RpbWUgLSAzMCA+PSAwLCBzdGFydGluZ190aW1lIC0gMzAsIDApCiAgdG9fdGltZSA8LSBjb21wbGV0ZV90aW1lCiAgICAKICBkZkJlZm9yZSA8LSBwRGF0YVtwRGF0YSRUaW1lIDwgc3RhcnRpbmdfcG9pbnRzW2lkeF0gJiBwRGF0YSRUaW1lID49IGZyb21fdGltZSxdCiAgZGZBZnRlciA8LSBwRGF0YVtwRGF0YSRUaW1lID49IHN0YXJ0aW5nX3BvaW50c1tpZHhdICsgMyAmIHBEYXRhJFRpbWUgPD0gdG9fdGltZSxdCiAgCiAgIyBkaWZmU3BlZWQgPC0gbWVhbihkZkFmdGVyJFNwZWVkKSAtIG1lYW4oZGZCZWZvcmUkU3BlZWQpCiAgYnJha2VNZWFuIDwtIG1lYW4oZGZBZnRlciRCcmFraW5nKQogIGJyYWtlU3RkIDwtIG1lYW4oZGZBZnRlciRCcmFraW5nKQogIAogIHBwTWVhbiA8LSBtZWFuKGRmQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKQogIHBwQmVmb3JlIDwtIG1lYW4oZGZCZWZvcmUkcHBMb2dOb3JtYWxpemVkKQogIHBwU3RkIDwtIHNkKGRmQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKQogIAogIG1pZF9hdmcgPC0gKHBwX2Jhc2VsaW5lW1twXV0gKyBtZWFuKGRmQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkpIC8gMgogICAgCiAgZGlmZlBQIDwtIG1lYW4oZGZBZnRlciRwcExvZ05vcm1hbGl6ZWQpIC0gbWVhbihkZkJlZm9yZSRwcExvZ05vcm1hbGl6ZWQpCiAgCiAgYmVoYXZpb3JhbE1hdHJpeFtpZHgsIF0gPC0gYyhwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGJyYWtlTWVhbiwgZGlnaXRzPTUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGJyYWtlU3RkLCBkaWdpdHM9NSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwcEJlZm9yZSwgZGlnaXRzPTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocHBNZWFuLCBkaWdpdHMgPSA1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBwU3RkLCBkaWdpdHM9NSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChkaWZmUFAsIGRpZ2l0cz01KSkKICBpZHggPC0gaWR4ICsgMQp9CgojIGJlaGF2aW9yYWxNYXRyaXgKCmJlaGF2aW9yYWxEZiA8LSBhcy5kYXRhLmZyYW1lKGJlaGF2aW9yYWxNYXRyaXgpCm5hbWVzKGJlaGF2aW9yYWxEZikgPC0gYmVoYXZpb3JhbENvbHVtbnMKCmJlaGF2aW9yYWxEZgoKYGBgCgoKYGBge3J9CmNsdXN0ZXJpbmdEZiA8LSBiZWhhdmlvcmFsRGYKY2x1c3RlcmluZ0RmJFN1YmplY3QgPC0gTlVMTAojIGNsdXN0ZXJpbmdEZiRQUF9kZXZfbm9ybSA8LSBhcy5udW1lcmljKGNsdXN0ZXJpbmdEZiRQUF9kZXYpIC8gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfdSkKIyBjbHVzdGVyaW5nRGYkUFBfc3RkX25vcm0gPC0gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfc3RkKSAvIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX3UpCmNsdXN0ZXJpbmdEZiRCcmFrZV91IDwtIE5VTEwKY2x1c3RlcmluZ0RmJEJyYWtlX3N0ZCA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRQUF9iZWZvcmUgPC0gTlVMTApjbHVzdGVyaW5nRGYkUFBfdSA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRQUF9zdGQgPC0gTlVMTAojIGNsdXN0ZXJpbmdEZiRQUF9kZXYgPC0gTlVMTAoKcm93bmFtZXMoY2x1c3RlcmluZ0RmKSA8LSBwYXN0ZTAoIiMiLCBwZXJzb25zKQoKZm9yIChjb2wgaW4gbmFtZXMoY2x1c3RlcmluZ0RmKSkgewogIGNsdXN0ZXJpbmdEZlssY29sXSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihjbHVzdGVyaW5nRGZbLCBjb2xdKSkKICBjbHVzdGVyaW5nRGZbLGNvbF0gPC0gc2NhbGUoY2x1c3RlcmluZ0RmWyxjb2xdKQp9CmNsdXN0ZXJpbmdEZgpgYGAKCmBgYHtyfQpsaWJyYXJ5KGRlbmRleHRlbmQpCgpOVU1CRVJfT0ZfQ0xVU1RFUlMgPSAzCgpjb2xvcl9kYXJrcGluayA9ICIjZTc1NDgwIgpDTFVTVEVSX0JSQU5DSF9DT0xPUlMgPC0gYygicmVkIiwgY29sb3JfZGFya3BpbmssICJibHVlIilbMTpOVU1CRVJfT0ZfQ0xVU1RFUlNdCkNMVVNURVJfTEFCRUxfQ09MT1JTIDwtIGMoInJlZCIsIGNvbG9yX2RhcmtwaW5rLCAiYmx1ZSIpWzE6TlVNQkVSX09GX0NMVVNURVJTXQoKYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcgPC0gYXMubWF0cml4KGNsdXN0ZXJpbmdEZikKcm93bmFtZXMoYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcpIDwtIHBhc3RlMCgiIyIsIHBlcnNvbnMpCmRpc3RNYXRyaXggPC0gZGlzdChiZWhhdmlvcmFsTWF0cml4Q2x1c3RlcmluZykKaHJlc3VsdHMgPC0gZGlzdE1hdHJpeCAlPiUgaGNsdXN0CgpoYyA8LSBocmVzdWx0cyAlPiUgCiAgICAgIGFzLmRlbmRyb2dyYW0gJT4lCiAgICAgIHNldCgibm9kZXNfY2V4IiwgTlVNQkVSX09GX0NMVVNURVJTKSAlPiUKICAgICAgc2V0KCJsYWJlbHNfY29sIiwgdmFsdWUgPSBDTFVTVEVSX0xBQkVMX0NPTE9SUywgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpICU+JQogICAgICAjIHNldCgibGVhdmVzX3BjaCIsIDE5KSAlPiUKICAgICAgIyBzZXQoImxlYXZlc19jb2wiLCB2YWx1ZSA9IGMoImdyYXkiKSwgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpICU+JSAgICAKICAgICAgc2V0KCJicmFuY2hlc19rX2NvbG9yIiwgdmFsdWU9Q0xVU1RFUl9CUkFOQ0hfQ09MT1JTLCBrPU5VTUJFUl9PRl9DTFVTVEVSUykKCnBsb3QoaGMpCmxlZ2VuZCgidG9wcmlnaHQiLCAKICAgICBsZWdlbmQgPSBjKCJFeGNlcHRpb25hbCBDaGlja2VuIiAsICJOb3JtYWwgQ2hpY2tlbiIgLCAiTm8tY2hhbmdlL0Nvb2wiKSwgCiAgICAgY29sID0gYygicmVkIiwgInBpbmsiICwgImJsdWUiKSwgCiAgICAgcGNoID0gYygyMCwyMCwyMCksIGJ0eSA9ICJuIiwgIHB0LmNleCA9IDEuNSwgY2V4ID0gMC44ICwgCiAgICAgdGV4dC5jb2wgPSAiYmxhY2siLCBob3JpeiA9IEZBTFNFLCBpbnNldCA9IGMoMCwgMC4xKSkKYGBgCmBgYHtyfQpkZkFjdGl2aXR5IDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRUaW1lPT02MCxdICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eSIpKQpnZXRBY3Rpdml0eU5hbWUgPC0gZnVuY3Rpb24oeCkgewogIGlmKHggPT0gMSkgcmV0dXJuKCJOb3JtYWwiKQogIGlmKHggPT0gMikgcmV0dXJuKCJDb2duaXRpdmUiKQogIGlmKHggPT0gMykgcmV0dXJuKCJNb3RvcmljIikKfQpkZkFjdGl2aXR5JEFjdGl2aXR5TmFtZSA8LSBzYXBwbHkoZGZBY3Rpdml0eSRBY3Rpdml0eSwgZ2V0QWN0aXZpdHlOYW1lKQpkZkFjdGl2aXR5JFN1YmplY3QgPC0gYXMuZmFjdG9yKGRmQWN0aXZpdHkkU3ViamVjdCkKcm93bmFtZXMoZGZBY3Rpdml0eSkgPC0gTlVMTApkZkFjdGl2aXR5ICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eU5hbWUiKSkKYGBgCgpgYGB7cn0KbGlicmFyeShjbHVzdGVyKQpmaXQgPC0ga21lYW5zKGNsdXN0ZXJpbmdEZiwgMikKY2x1c3Bsb3QoY2x1c3RlcmluZ0RmLCBmaXQkY2x1c3RlciwgY29sb3I9VFJVRSwgc2hhZGU9VFJVRSwKICAgbGFiZWxzPTIsIGxpbmVzPTApCmBgYAoKYGBge3J9CnNpbGhvdWV0dGVfc2NvcmUgPC0gZnVuY3Rpb24oayl7CiAga20gPC0ga21lYW5zKGNsdXN0ZXJpbmdEZiwgY2VudGVycyA9IGssIG5zdGFydD0yNSkKICBzcyA8LSBzaWxob3VldHRlKGttJGNsdXN0ZXIsIGRpc3QoY2x1c3RlcmluZ0RmKSkKICBtZWFuKHNzWywgM10pCn0KayA8LSAyOjEwCmF2Z19zaWwgPC0gc2FwcGx5KGssIHNpbGhvdWV0dGVfc2NvcmUpCnBsb3QoaywgdHlwZT0nYicsIGF2Z19zaWwsIHhsYWI9J051bWJlciBvZiBjbHVzdGVycycsIHlsYWI9J0F2ZXJhZ2UgU2lsaG91ZXR0ZSBTY29yZXMnLCBmcmFtZT1GQUxTRSkKYGBgCgoKYGBge3J9CiMgcGxvdChjbHVzdGVyaW5nRGYkUFBfRGV2KQojIGdncGxvdChjbHVzdGVyaW5nRGYsIGFlcyhQUF9NZWFuKSkgKyAKIyAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykKYGBgCgpgYGB7cn0KIyBwbG90KGNsdXN0ZXJpbmdEZiRQUF9EZXYpCiMgZ2dwbG90KGNsdXN0ZXJpbmdEZiwgYWVzKFBQX0RldikpICsgCiMgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpCmBgYAoKIyMjIE1MIE1vZGVsCmBgYHtyfQpkZl9Ecml2ZTMgPC0gcmVhZC5jc3YoJy4uL2RhdGEvb3V0cHV0L0RyaXZlXzMvY29ycl9QcmV2XzE1c19OZXh0XzVzLmNzdicpCmRmX0RyaXZlMyRTdWJqZWN0IDwtIGFzLmZhY3RvcihkZl9Ecml2ZTMkU3ViamVjdCkKCkNMVVNURVJfVEhSRVNIT0xEID0gNApjbHVzdGVycyA8LSBjdXRyZWUoaHJlc3VsdHMsIGg9Q0xVU1RFUl9USFJFU0hPTEQpCmdldENsdXN0ZXJOYW1lIDwtIGZ1bmN0aW9uKHMpIHsKICBzSUQgPC0gc3RyX3JlcGxhY2UocywgIlN1YmplY3QgIiwgIiIpCiAgaWYgKHN0cl9zdWIoc0lEWzFdLCAxLDEpICE9ICIjIikgewogICAgc0lEIDwtIHBhc3RlMCgiIyIsIHNJRCkKICB9CiAgcmV0dXJuKGlmZWxzZShjbHVzdGVyc1tzSURdID09IDEsIDEsIDApKQp9Cgpjb2xDbGFzcyA8LSBzYXBwbHkocGVyc29ucywgZ2V0Q2x1c3Rlck5hbWUpCmRmX0RyaXZlMyRjbHNQUCA8LSBhcy5mYWN0b3IoY29sQ2xhc3MpCmBgYAoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJNTG1ldHJpY3MiKQojIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoUk9DUikKbGlicmFyeShjYXJldCkKCiMgVHJhaW4KdHJhaW5BbmRUZXN0TW9kZWwgPC0gZnVuY3Rpb24ocERhdGEpIHsKICBwU2VsZWN0ZWQgPC0gcERhdGEKICBwU2VsZWN0ZWQkU3ViamVjdCA8LSBOVUxMCiAgIyBwU2VsZWN0ZWQkYFN0ZWVyaW5nLi5zdGQuLmAgPC0gTlVMTAogIAogICMgVmlldyhwU2VsZWN0ZWQpCiAgCiAgbkNoaWNrZW4gPSBucm93KHBTZWxlY3RlZFtwU2VsZWN0ZWQkY2xzUFAgPT0gMSxdKQogIG5Ob3JtYWwgPSBucm93KHBTZWxlY3RlZFtwU2VsZWN0ZWQkY2xzUFAgPT0gMCxdKQogIAogIHByaW50KHBhc3RlKCJDaGlja2VuID0iLCBuQ2hpY2tlbikpCiAgcHJpbnQocGFzdGUoIk5vcm1hbCA9Iiwgbk5vcm1hbCkpCiAgCiAgIyBTcGxpdCBkYXRhc2V0CiAgc2V0LnNlZWQoNDMpCiAgbl9mb2xkcyA9IDQKICAKICBmb2xkcyA8LSBjcmVhdGVGb2xkcyhmYWN0b3IocFNlbGVjdGVkJGNsc1BQKSwgayA9IG5fZm9sZHMsIGxpc3QgPSBGQUxTRSkKICAjIGZvbGRzIDwtIGN1dChzZXEoMSxucm93KHBTZWxlY3RlZCkpLCBicmVha3M9bl9mb2xkcyxsYWJlbHM9RkFMU0UpCiAgcFNlbGVjdGVkJGZvbGQgPC0gZm9sZHMKICAKICBrZl9hY2MgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfcHJlYyA8LSBkb3VibGUobl9mb2xkcykKICBrZl9yZWNhbGwgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfZjEgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfbnB2IDwtIGRvdWJsZShuX2ZvbGRzKQogIAogIGZvciAoaSBpbiAxOm5fZm9sZHMpIHsKICAgICMgdHJhaW5faW5kIDwtIHNhbXBsZShzZXFfbGVuKG5yb3cocFNlbGVjdGVkKSksIHNpemUgPSBzbXBfc2l6ZSkKICAgICMgdHJhaW4gPC0gcFNlbGVjdGVkW3RyYWluX2luZCwgXQogICAgIyB0ZXN0IDwtIHBTZWxlY3RlZFstdHJhaW5faW5kLCBdCiAgICAKICAgICMgdGVzdF9pbmQgPC0gd2hpY2goZm9sZHM9PWksIGFyci5pbmQ9VCkKICAgIHRyYWluIDwtIHBTZWxlY3RlZFtwU2VsZWN0ZWQkZm9sZCAhPSBpLCBdICU+JSBzZWxlY3QoLWZvbGQpCiAgICB0ZXN0IDwtIHBTZWxlY3RlZFtwU2VsZWN0ZWQkZm9sZCA9PSBpLCBdICU+JSBzZWxlY3QoLWZvbGQpCiAgICAKICAgIHByaW50KHBhc3RlKCJUcmFpbiA9IiwgbnJvdyh0cmFpbiksICJQb3MgPSIsIG5yb3codHJhaW5bdHJhaW4kY2xzUFAgPT0gMSxdKSwgIk5lZyA9IiwgbnJvdyh0cmFpblt0cmFpbiRjbHNQUCA9PSAwLF0pKSkKICAgIHByaW50KHBhc3RlKCJUZXN0ID0iLCBucm93KHRlc3QpLCAiUG9zID0iLCBucm93KHRlc3RbdGVzdCRjbHNQUCA9PSAxLF0pLCAiTmVnID0iLCBucm93KHRlc3RbdGVzdCRjbHNQUCA9PSAwLF0pKSkKICAgIAogICAgIyBNb2RlbCBUcmFpbgogICAgbW9kZWwgPC0gcmFuZG9tRm9yZXN0KGNsc1BQfi4sIGRhdGEgPSB0cmFpbiwgaW1wb3J0YW5jZSA9IFRSVUUsIG50cmVlPTEwKQogICAgcHJpbnQoaW1wb3J0YW5jZShtb2RlbCkpCiAgICAjIHByaW50KG1vZGVsKQogICAgCiAgICAjIFRlc3QKICAgIHRlc3RYIDwtIHNlbGVjdCh0ZXN0LCAtY2xzUFApCiAgICBwcmVkWSA8LSBwcmVkaWN0KG1vZGVsLCB0ZXN0WCkKICAgIHRlc3RZIDwtIHRlc3QkY2xzUFAKICAgIAogICAgI3ByaW50KGxldmVscyhwcmVkWSkpCiAgICAjcHJpbnQobGV2ZWxzKHRlc3RZKSkKICAgIAogICAgIyBFdmFsdWF0ZQogICAgIyBwcmludCh0YWJsZShwcmVkWSwgdGVzdFkpKQogICAgCiAgICBrZl9hY2NbaV0gPC0gbWVhbihwcmVkWT09dGVzdFkpCiAgICBrZl9yZWNhbGxbaV0gPC0gc2Vuc2l0aXZpdHkocHJlZFksIHRlc3RZKQogICAga2ZfcHJlY1tpXSA8LSBwb3NQcmVkVmFsdWUocHJlZFksIHRlc3RZLCBwb3NpdGl2ZSA9IDEpCiAgICBrZl9mMVtpXSA8LSAoMiAqIGtmX3JlY2FsbFtpXSAqIGtmX3ByZWNbaV0pIC8gKGtmX3JlY2FsbFtpXSArIGtmX3ByZWNbaV0pCiAgICBrZl9ucHZbaV0gPC0gbmVnUHJlZFZhbHVlKHByZWRZLCB0ZXN0WSwgcG9zaXRpdmUgPSAxKQogICAgCiAgICAjIHByaW50KHBhc3RlKCJQZXJmOiIsIGtmX2FjY1tpXSwga2ZfcmVjYWxsW2ldLCBrZl9wcmVjW2ldLCBrZl9mMVtpXSwga2ZfbnB2W2ldKSkKICB9CiAgCiAgIyBYR0IKICBwYXJhbSA8LSBsaXN0KG9iamVjdGl2ZSAgICAgICA9ICJiaW5hcnk6bG9naXN0aWMiLCAKICAgICAgICAgICAgICAgYm9vc3RlciAgICAgICAgICA9ICJnYnRyZWUiLAogICAgICAgICAgICAgICBldmFsX21ldHJpYyAgICAgID0gImF1YyIsCiAgICAgICAgICAgICAgIGV0YSAgICAgICAgICAgICAgPSAwLjEsCiAgICAgICAgICAgICAgIG1heF9kZXB0aCAgICAgICAgPSA1LAogICAgICAgICAgICAgICBnYW1tYSAgICAgICAgICAgID0wLjgsCiAgICAgICAgICAgICAgIG1pbl9jaGlsZF93ZWlnaHQgPSAzLAogICAgICAgICAgICAgICBzdWJzYW1wbGUgICAgICAgID0gMSwKICAgICAgICAgICAgICAgY29sc2FtcGxlX2J5dHJlZSA9IDAuNSwKICAgICAgICAgICAgICAgc3RyYXRpZmllZCAgICAgICA9IEYKICApCiAgcFNlbGVjdGVkIDwtIHBTZWxlY3RlZCAlPiUgbXV0YXRlKGNsc1BQPWlmZWxzZShjbHNQUD09MSwgMSwgMCkpCiAgCiAgIyBBVUMKICAjIGF1Y3MgPSBjKCkKICAjIHhnYl9tID0geGdiLmN2KCAgIHBhcmFtcyAgICAgICAgICAgICAgID0gcGFyYW0sCiAgIyAgICAgICAgICAgICAgICAgICBkYXRhID0gYXMubWF0cml4KHBTZWxlY3RlZCAlPiUgc2VsZWN0KC1jbHNQUCkpICwKICAjICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIHBTZWxlY3RlZCRjbHNQUCwKICAjICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgICAgICAgICAgICAgPSA1MDAsCiAgIyAgICAgICAgICAgICAgICAgICB2ZXJib3NlICAgICAgICAgICAgID0gRiwKICAjICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb24gICAgICAgICAgPSBULAogICMgICAgICAgICAgICAgICAgICAgbWF4aW1pemUgICAgICAgICAgICA9IFQsCiAgIyAgICAgICAgICAgICAgICAgICBuZm9sZCA9IG5fZm9sZHMsCiAgIyAgICAgICAgICAgICAgICAgICBtZXRyaWNzICA9ICJhdWMiLAogICMgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gMTAwLAogICMgICAgICAgICAgICAgICAgICAgc2NhbGVfcG9zX3dlaWdodCA9IDEpCiAgIyBhdWNzID0gIGMoYXVjcyxhcy5udW1lcmljKHhnYl9tJGV2YWx1YXRpb25fbG9nW3hnYl9tJGJlc3RfaXRlcmF0aW9uLCJ0ZXN0X2F1Y19tZWFuIl0pKQogIAogICMgR2V0IGF2ZXJhZ2UgcGVyZm9ybWFuY2UKICBhY2MgPC0gbWVhbihrZl9hY2MpCiAgcHJlYyA8LSBtZWFuKGtmX3ByZWMpCiAgcmVjYWxsIDwtIG1lYW4oa2ZfcmVjYWxsKQogIGYxIDwtIG1lYW4oa2ZfZjEpCiAgbnB2IDwtIG1lYW4oa2ZfbnB2KQogICMgYXVjIDwtIG1lYW4oYXVjcykKIAogICMgUmV0dXJuIAogIHJ0biA8LSBsaXN0KAogICAgYWNjdXJhY3k9YWNjLAogICAgcmVjYWxsPXJlY2FsbCwKICAgIHByZWNpc2lvbj1wcmVjLAogICAgZjE9ZjEsCiAgICBucHY9bnB2CiAgICAjIGF1Yz1hdWMKICApCiAgCiAgcHJpbnQocnRuKQp9Cgp0cmFpbkFuZFRlc3RNb2RlbChkZl9Ecml2ZTMpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KFZHQU0pCgpOVU1fRkVBVFVSRVMgPSA4CgpwU2VsZWN0ZWQgPC0gZGZfRHJpdmUzCnBTZWxlY3RlZCRTdWJqZWN0IDwtIE5VTEwKCmZpdCA8LSB2Z2xtKGNsc1BQfi4sIGZhbWlseT1tdWx0aW5vbWlhbCwgZGF0YT1wU2VsZWN0ZWQpCiMgc3VtbWFyaXplIHRoZSBmaXQKc3VtbWFyeShmaXQpCgpwcm9iYWJpbGl0aWVzIDwtIHByZWRpY3QoZml0LCBwU2VsZWN0ZWRbLDE6TlVNX0ZFQVRVUkVTXSwgdHlwZT0icmVzcG9uc2UiKQpwcmVkaWN0aW9ucyA8LSBhcHBseShwcm9iYWJpbGl0aWVzLCAxLCB3aGljaC5tYXgpCnByZWRpY3Rpb25zW3doaWNoKHByZWRpY3Rpb25zPT0iSGlnaCIpXSA8LSBsZXZlbHMocFNlbGVjdGVkJGNsc1BQKVsxXQpwcmVkaWN0aW9uc1t3aGljaChwcmVkaWN0aW9ucz09IkxvdyIpXSA8LSBsZXZlbHMocFNlbGVjdGVkJGNsc1BQKVsyXQojIHN1bW1hcml6ZSBhY2N1cmFjeQp0YWJsZShwcmVkaWN0aW9ucywgcFNlbGVjdGVkJGNsc1BQKQoKIyB4IDwtIHBTZWxlY3RlZFssMTpOVU1fRkVBVFVSRVNdCiMgeSA8LSBwU2VsZWN0ZWRbLE5VTV9GRUFUVVJFUyArIDFdCiMgIyBmaXQgbW9kZWwKIyBmaXQgPC0gcGxzZGEoeCwgeSwgcHJvYk1ldGhvZD0iQmF5ZXMiKQojICMgc3VtbWFyaXplIHRoZSBmaXQKIyBzdW1tYXJ5KGZpdCkKIyAjIG1ha2UgcHJlZGljdGlvbnMKIyBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGZpdCwgcFNlbGVjdGVkWywxOk5VTV9GRUFUVVJFU10pCiMgIyBzdW1tYXJpemUgYWNjdXJhY3kKIyB0YWJsZShwcmVkaWN0aW9ucywgcFNlbGVjdGVkJGNsc1BQKQoKYGBgCgoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCgpwU2VsZWN0ZWQgPC0gZGZfRHJpdmUzCnBTZWxlY3RlZCRTdWJqZWN0IDwtIE5VTEwKcFNlbGVjdGVkJGNsc1BQIDwtIGFzLm51bWVyaWMoY29sQ2xhc3MpCgojIGRlZmluZSB0cmFpbmluZyBjb250cm9sCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDQpCgojIHRyYWluIHRoZSBtb2RlbCBvbiB0cmFpbmluZyBzZXQKbW9kZWwgPC0gdHJhaW4oY2xzUFAgfiAuLAogICAgICAgICAgICAgICBkYXRhID0gcFNlbGVjdGVkLAogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLAogICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xtIiwKICAgICAgICAgICAgICAgZmFtaWx5PWJpbm9taWFsKCkpCgojIHByaW50IGN2IHNjb3JlcwpzdW1tYXJ5KG1vZGVsKQoKcHJlZFRyYWluID0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YT1wU2VsZWN0ZWQsIHR5cGU9InJhdyIpCnRhYmxlKHBTZWxlY3RlZCRjbHNQUCwgcHJlZFRyYWluID4gMC41KQoKYGBgCgpgYGB7cn0KZm9ybXVsYSA9IGNsc1BQIH4gLgptb2RlbGxtIDwtIGdsbShmb3JtdWxhLCBkYXRhPXBTZWxlY3RlZCkKcGxvdChtb2RlbGxtKQpzdW1tYXJ5KG1vZGVsKQpgYGAKCgo=